package cn.changeforyou.web.cloud.webBase.utils.bean;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.beans.BeanCopier;
import net.sf.cglib.beans.BeanMap;
import org.apache.commons.lang3.ObjectUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * BeanCopier工具类
 *
 * @author 李锋镝, zhyu
 */
@Slf4j
public class BeanCopierUtil {

    /**
     * BeanCopier的缓存
     */
    static final ConcurrentHashMap<String, BeanCopier> BEAN_COPIER_CACHE = new ConcurrentHashMap<>();
    static ObjectMapper objectMapper = new ObjectMapper();
    static {
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    /**
     * 复制list
     *
     * @param source
     * @param target
     * @return
     */
    public static <T,R> List<T> copyList(List<R> source, Class<T> target) {
        if (!ObjectUtils.allNotNull(source, target)) {
            return null;
        }
        ArrayList<T> newList = new ArrayList();
        try {
            for (R o : source) {
                T t = copyOne(o, target);
                newList.add(t);
            }
            return newList;
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                log.error("error", e);
            }
        }
        return null;
    }



    private static <T, R> void copySupers(T t, R source) throws IllegalAccessException {
        Class<?> superclass = source.getClass().getSuperclass();
        if (superclass == Object.class) {
            return;
        }
        Field[] declaredFields = superclass.getDeclaredFields();
        if (null != declaredFields && 0 != declaredFields.length) {
            Class<?> aClass = t.getClass();
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                Object o = declaredField.get(source);
                String name = declaredField.getName();
                try {
                    Field declaredField1 = aClass.getDeclaredField(name);
                    declaredField1.setAccessible(true);
                    declaredField1.set(t, o);
                } catch (NoSuchFieldException e) {
                    continue;
                }
            }
        }
    }

    private static void copySupersFiled(Object t, Object source) throws IllegalAccessException {
        Class<?> superclass = source.getClass().getSuperclass();
        if (superclass == Object.class) {
            return;
        }
        Field[] declaredFields = superclass.getDeclaredFields();
        if (null != declaredFields && 0 != declaredFields.length) {
            Class<?> aClass = t.getClass();
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                Object o = declaredField.get(source);
                String name = declaredField.getName();
                try {
                    Field declaredField1 = aClass.getDeclaredField(name);
                    declaredField1.setAccessible(true);
                    declaredField1.set(t, o);
                } catch (NoSuchFieldException e) {
                    continue;
                }
            }
        }
    }

    /**
     * 复制单个obj
     *
     * @param source
     * @param target
     * @return
     */
    public static <T, R>T copyOne(R source, Class<T> target) {
        if (!ObjectUtils.allNotNull(source, target)) {
            return null;
        }
        try {
            T o1 = target.newInstance();
            copy(source, o1);
            if (o1 instanceof VoTinker) {
                VoTinker o11 = (VoTinker) o1;
                o11.tikner(source);
            }
            copySupers(o1, source);
            return o1;
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                log.error("error", e);
            }
        }
        return null;
    }


    public static Object copyWithVotinker(Object source, Class target) {
        if (!ObjectUtils.allNotNull(source, target)) {
            return null;
        }
        try {
            Object o1 = target.newInstance();
            copyWithVotinker(source, o1);
            if (o1 instanceof VoTinker) {
                VoTinker o11 = (VoTinker) o1;
                o11.tikner(source);
            }
            copySupersFiled(o1, source);
            return o1;
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                log.error("error", e);
            }
        }
        return null;
    }

    /**
     * 任意复制
     *
     * @param source
     * @param target
     * @return
     */
    public static <T, R> Object copyAuto(R source, Class<T> target) {
        return copyAuto(source, target, false);
    }

    /**
     * 任意复制
     *
     * @param source
     * @param target
     * @param returnResouceList 是否返回原来的list
     * @return
     */
    public static <T, R>Object copyAuto(R source, Class<T> target, boolean returnResouceList) {
        if (!ObjectUtils.allNotNull(source, target)) {
            return null;
        }
        if (source instanceof List) {
            if (!returnResouceList) {
                return copyList((List) source, target);
            } else {
                List sourceList = (List) source;
                List copyList = copyList((List) source, target);
                sourceList.clear();
                sourceList.addAll(copyList);
                return sourceList;
            }
        } else {
            return copyOne(source, target);
        }
    }

    /**
     * BeanCopier的copy
     *
     * @param source 源文件的
     * @param target 目标文件
     */
    public static <T, R>void copy(R source, T target) {
        String key = BeanCopierUtil.genKey(source.getClass(), target.getClass());
        BeanCopier beanCopier;
        if (BEAN_COPIER_CACHE.containsKey(key)) {
            beanCopier = BEAN_COPIER_CACHE.get(key);
        } else {
            beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false);
            BEAN_COPIER_CACHE.put(key, beanCopier);
        }
        beanCopier.copy(source, target, null);
    }



    /**
     * BeanCopier的copy
     *
     * @param source 源文件的
     * @param target 目标文件
     */
    public static void copyWithVotinker(Object source, Object target) {
        String key = genKey(source.getClass(), target.getClass());
        BeanCopier beanCopier;
        if (BEAN_COPIER_CACHE.containsKey(key)) {
            beanCopier = BEAN_COPIER_CACHE.get(key);
        } else {
            beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false);
            BEAN_COPIER_CACHE.put(key, beanCopier);
        }
        beanCopier.copy(source, target, null);
    }

    /**
     * 将javabean对象转换为map
     */
    public static <T> Map<String, Object> beanToMap(T bean) {
        Map<String, Object> map = new HashMap<>();
        if (bean != null) {
            BeanMap beanMap = BeanMap.create(bean);
            for (Object key : beanMap.keySet()) {
                Object value = beanMap.get(key);
                map.put(key + "", value);
            }
        }
        return map;
    }

    public static <T> T mapToBeanWithJackson(Map map, Class<T> clazz) throws Exception {
        String json = objectMapper.writeValueAsString(map);
        return objectMapper.readValue(json, clazz);
    }


    public static <T> T mapToBean(Map map, Class<T> clazz) throws Exception {
        T bean = clazz.newInstance();
        BeanMap beanMap = BeanMap.create(bean);
        beanMap.putAll(map);
        return bean;
    }

    /**
     * 将map转换为javabean对象
     */
    public static <T> T mapToBeanWithObject(Map<String, Object> map, T bean) {
        if (map != null) {
            BeanMap beanMap = BeanMap.create(bean);
            beanMap.putAll(map);
        }
        return bean;
    }


    /**
     * 生成key
     *
     * @param srcClazz 源文件的class
     * @param tgtClazz 目标文件的class
     * @return string
     */
    private static String genKey(Class<?> srcClazz, Class<?> tgtClazz) {
        return srcClazz.getName() + tgtClazz.getName();
    }
}
